package org.lc.service.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import org.lc.platform.base.enums.RedisNames;
import org.lc.platform.base.exception.BusinessException;
import org.lc.platform.base.exception.SystemException;
import org.lc.platform.base.vo.PageVo;
import org.lc.platform.mybatis.crud.service.impl.CrudBaseServiceImpl;
import org.lc.platform.mybatis.manager.query.QueryBuilder;
import org.lc.platform.redis.service.CacheService;
import org.lc.service.system.dto.SysDictDetailDto;
import org.lc.service.system.dto.SysDictDto;
import org.lc.service.system.entity.SysDict;
import org.lc.service.system.entity.SysDictDetail;
import org.lc.service.system.mapper.SysDictMapper;
import org.lc.service.system.service.SysDictService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;


@Service
public class SysDictServiceImpl extends CrudBaseServiceImpl<SysDictDto, SysDict, SysDictMapper, SysDict> implements SysDictService {

    private final CacheService cache;

    public SysDictServiceImpl(CacheService cache) {
        this.cache = cache;
    }

    public boolean handleCache(Boolean cache, String code) {
        var key = RedisNames.DICTIONARY.getValue() + code;
        if (cache) {
            var data = getDictEnumListByCode(code);
            this.cache.setList(key, data);
        } else {
            this.cache.remove(key);
        }
        return true;
    }

    @Override
    public Boolean createBaseOne(SysDict body) {
        handleCache(body.getCache(), body.getCode());
        return super.createBaseOne(body);
    }

    @Override
    public SysDict Convertor(SysDict data) {
        return BeanUtil.copyProperties(data, SysDict.class);
    }

    @Override
    public QueryBuilder<SysDict> getQueryBuilder(SysDictDto query, QueryBuilder<SysDict> queryBuilder) {
        queryBuilder.eq(StrUtil.isNotEmpty(query.getName()), "name", query.getName());
        return queryBuilder;
    }

    @Override
    public boolean getCodeExits(String dict) {
        var warp = new LambdaQueryWrapper<SysDict>();
        warp.eq(dict != null, SysDict::getCode, dict);
        return this.baseMapper.exists(warp);
    }

    @Override
    public List<SysDictDetail> getDictEnumListByCode(String code) {
        var key = RedisNames.DICTIONARY.getValue() + code;
        List<SysDictDetail> result = this.cache.getList(key);
        if (CollectionUtil.isNotEmpty(result)) {
            return result;
        }
        var warp = Db.lambdaQuery(SysDict.class).eq(code != null, SysDict::getCode, code);
        var dict = this.baseMapper.selectOne(warp.getWrapper());
        if (dict == null) {
            return List.of();
        }
        var dictDetail = Db.list(Db.lambdaQuery(SysDictDetail.class).eq(SysDictDetail::getDictId, dict.getId()).getWrapper());
        if (dictDetail == null) {
            return List.of();
        }
        return dictDetail.stream().peek(r -> {
            var type = r.getType();
            var value = r.getValue();
            try {
                switch (type) {
                    case "boolean":
                        r.setValue(Convert.toBool(value));
                        break;
                    case "int":
                        r.setValue(Convert.toInt(value));
                        break;
                    case "float":
                        r.setValue(Convert.toFloat(value));
                        break;
                    case "empty":
                        r.setValue(null);
                        break;
                    case "json":
                        r.setValue(JSONUtil.parseObj(value));
                        break;
                    case "date":
                        r.setValue(LocalDateTime.parse((CharSequence) value));
                    default:
                        break;
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }).toList();
    }

    @Override
    public PageVo<SysDictDetail> getDictDetail(String dict, SysDictDetailDto query) {
        var queryBuilder = Db.lambdaQuery(SysDictDetail.class);
        queryBuilder.eq(SysDictDetail::getDictId, dict);
        queryBuilder.like(query.getLabel() != null, SysDictDetail::getLabel, query.getLabel());
        Page<SysDictDetail> page = query.toMapPage();
        Page<SysDictDetail> p = null;
        if (query.getIsPage()) {
            p = queryBuilder.page(page);
        } else {
            p = new Page<>();
            p.setRecords(queryBuilder.list());
        }
        return PageVo.of(p);
    }

    @Override
    public SysDictDetail getDictDetailOne(String detail) {
        var queryBuilder = Db.lambdaQuery(SysDictDetail.class);
        queryBuilder.eq(SysDictDetail::getId, detail);
        return queryBuilder.one();
    }

    @Override
    public boolean createDictDetail(SysDictDetail body) {
        return Db.save(body);
    }

    @Override
    public boolean updateDictDetail(String id, SysDictDetail body) {
        body.setId(id);
        return Db.updateById(body);
    }

    @Override
    public boolean deleteDictDetailOne(String id) {
        return Db.removeById(id, SysDictDetail.class);
    }

    @Override
    @Transactional
    public boolean deleteDetailMany(List<String> ids) {
        try {
            String regex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$";
            boolean isFailed = ids.stream().anyMatch(r -> !r.matches(regex));
            if (isFailed) {
                throw new BusinessException(("数组含有非法UUID"));
            }
        } catch (Exception e) {
            throw new SystemException((e.getMessage()));
        }
        return Db.removeByIds(ids, SysDictDetail.class);
    }
}